#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <vector>

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

//XYZ point where camera looks
float centerX=0,centerY=0,centerZ=-10000000;
// XYZ position of the camera
float eyeX=0.0f,eyeY=10.0f,eyeZ=20.0f;
//bike position
float bikeXpos=0, bikeYPos = 1.5, bikeZpos=0;
//bike size
float bikeHeight=3, bikeWidth = 3, bikeLength = 5;
int buildingHeights[100];
float xBallOffSets[100];
void changeSize(int w, int h) {

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0)
		h = 1;
	float ratio =  w * 1.0 / h;

	// Use the Projection Matrix
	glMatrixMode(GL_PROJECTION);

	// Reset Matrix
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45.0f, ratio, 0.1f, 1000.0f);

	// Get Back to the Modelview
	glMatrixMode(GL_MODELVIEW);
}

int trackWidth = 20;
int trackLength = 500;
int buildingSize = 10;
int offsetFromDistanceTravelled;
float speed = .1f;
bool isMoving = true;
float ballPosition[5][3];
int numberOfCollision =0;
int currentBall;
int nextBall;
int score;
int font=(int)GLUT_BITMAP_HELVETICA_18;

void renderBitmapString(float x, float y,float z, void *font,char *string)
{

	char *c;
	// set position to start drawing fonts
	glRasterPos3f(x, y,z);
	// loop all the characters in the string
	for (c=string; *c != '\0'; c++) {
		glutBitmapCharacter(font, *c);
	}
}

void drawBuildings() 
{

	glPushMatrix();
	//glColor3f(.4,.4,.4);
	//int buildingHeight = 20;//rand()%10 + 10;
	
	for (int i=offsetFromDistanceTravelled; i<50+offsetFromDistanceTravelled; i++)
	{
		glPushMatrix();
		glColor3f(i/50.0,i/50.0,i/50.0);
		int buildingHeight = buildingHeights[i%50];
		glTranslatef(trackWidth/2+buildingSize/2,buildingHeight/2,-buildingSize/2);
		glTranslatef(0,0,-i*buildingSize+1);
		glScalef(buildingSize,buildingHeight,buildingSize);
		glutSolidCube(1);
		glTranslatef(0,0,-1);
		glPopMatrix();

	}
	for (int i=offsetFromDistanceTravelled; i<50+offsetFromDistanceTravelled; i++)
	{
		glPushMatrix();
		glColor3f(i/50.0,i/50.0,i/50.0);
		int buildingHeight = buildingHeights[i%50+50];
		glTranslatef(-(trackWidth/2+buildingSize/2),buildingHeight/2,-buildingSize/2);
		glTranslatef(0,0,-i*buildingSize);
		glScalef(buildingSize,buildingHeight,buildingSize);
		glutSolidCube(1);
		glTranslatef(0,0,-1);
		glPopMatrix();

	}

	glPopMatrix();
}

void drawTrack() 
{
	//draw track
	glPushMatrix();
	glColor3f(.5,.5,.5);
	for (int i=offsetFromDistanceTravelled; i<50+offsetFromDistanceTravelled; i++)
	{
		glPushMatrix();
		glScalef(trackWidth,.1,trackLength/50);
		glTranslatef(0,0,-i);
		glutSolidCube(1);
		glPopMatrix();
	}
	glPopMatrix();
}

void detectBalltoBikeCollisions() 
{
	float bl,br,bt,bb, ml,mr,mt,mb;
	br=ballPosition[1][0]+0.5;
	bl=ballPosition[1][0]-0.5;
	bt=ballPosition[1][2]-0.5;
	bb=ballPosition[1][2]+0.5;

	ml=bikeXpos-bikeLength/2;
	mr=bikeXpos+bikeLength/2;
	mt=bikeZpos-bikeLength/2 -15;
	mb=bikeZpos+bikeLength/2 -15;
	int testBall= ballPosition[1][2];
	if(mt<bb && mb>bt && mr>bl && ml<br){
		if(currentBall != testBall){
			currentBall = ballPosition[1][2];
			numberOfCollision++;
			speed =.1f;
		}
	}
}

void drawBalls() 
{
	//draw Obstacles
	offsetFromDistanceTravelled = -(bikeZpos-1)/100;
	glPushMatrix();
	for (int i=offsetFromDistanceTravelled; i<5+offsetFromDistanceTravelled; i++)
	{
		glPushMatrix();
		//int xOffSet;
		//if(xBallOffSets[i]==0){
		//	xOffSet=0;
		//}
		//if(xBallOffSets[i]==1){
		//	xOffSet=trackWidth/3;
		//}
		//if(xBallOffSets[i]==2){
		//	xOffSet=-trackWidth/3;
		//}
		ballPosition[i-offsetFromDistanceTravelled][0] = xBallOffSets[i%100]*2;
		ballPosition[i-offsetFromDistanceTravelled][1] = 1;
		ballPosition[i-offsetFromDistanceTravelled][2] = 20-i*100;
		glTranslatef(
			ballPosition[i-offsetFromDistanceTravelled][0],
			ballPosition[i-offsetFromDistanceTravelled][1],
			ballPosition[i-offsetFromDistanceTravelled][2]);
		glutSolidSphere(1,32,32);
		glPopMatrix();
	}
	glPopMatrix();
}

void drawText() 
{
	char scoreBuffer[100]; //Build FPS TEXT
	char scoreText[25] = "score: ";
	itoa(score,scoreBuffer,10);
	strcat_s(scoreText,scoreBuffer);
	renderBitmapString(0,30,-100,(void *)font,scoreText);

	char collisionsBuffer[100]; //Build FPS TEXT
	char collisionsText[25] = "collisions: ";
	itoa(numberOfCollision,collisionsBuffer,10);
	strcat_s(collisionsText,collisionsBuffer);
	renderBitmapString(0,35,-100,(void *)font,collisionsText);
}

void drawBike() 
{
	//draw bike
	glPushMatrix();
	glTranslatef(bikeXpos,bikeYPos,bikeZpos-15);
	glColor3f(1,0,0);
	glScalef(bikeWidth,bikeHeight,bikeLength);
	glutSolidCube(1);
	/*glTranslatef(0,0,2);
	glColor3f(0,0,1);
	glutSolidCube(1);*/
	glPopMatrix();
}

void renderScene(void) {

	// Clear Color and Depth Buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0,191.0/255.0,1,1);//sky blue
	// Reset transformations
	glLoadIdentity();

	// Set the camera
	gluLookAt(	eyeX, eyeY, eyeZ+bikeZpos,
		centerX, centerY,  centerZ,
		0.0f, 1.0f,  0.0f);
	if(isMoving){
		bikeZpos += (-speed);
		speed+=0.0001;
	}

	offsetFromDistanceTravelled = -(bikeZpos-1)/10;
	drawTrack();
	drawBuildings();
	drawBalls();
	detectBalltoBikeCollisions();
	drawBike();
	drawText();

	score = -bikeZpos;

	glFlush();	
	glutSwapBuffers();
}

void processNormalKeys(unsigned char key, int x, int y) {
	switch(key){
	case 'b':
		eyeZ += 10;
		break;
	case 'f':
		eyeZ -= 10;
		break;
	case 27:
		exit(0);
		break;
	}

		//if(key== 's'){
		//	bikeZpos+=2;
		//}
		//if(key== 'w'){
		//	bikeZpos-=2;
		//}
		if(key== 'd'){
			
			if(bikeXpos<(trackWidth/2 - 3)){
				bikeXpos+=1;
			}
			//bikeXpos+=trackWidth/3;
		}
		if(key== 'a'){
			if(bikeXpos>-(trackWidth/2 - 3)){
				bikeXpos-=1;
			}
			//bikeXpos-=trackWidth/3;
		}
		if(key== 'q'){
			isMoving=!isMoving;
		}

}

void processSpecialKeys(int key, int xx, int yy) {
	float angle= 0.1;
	float x2 = 0.0f, y2 =0.0f, z2=0.0f;

	switch (key) {
	case GLUT_KEY_LEFT :
		x2 = eyeX*cos(angle) - eyeZ*sin(angle);
		z2 = eyeX*sin(angle) + eyeZ*cos(angle); 
		eyeX=x2;
		eyeZ=z2;
		break;
	case GLUT_KEY_RIGHT :
		x2 = eyeX*cos(-angle) - eyeZ*sin(-angle);
		z2 = eyeX*sin(-angle) + eyeZ*cos(-angle); 
		eyeX=x2;
		eyeZ=z2;
		break;
	case GLUT_KEY_UP :
		y2 = eyeY*cos(angle) - eyeZ*sin(angle);
		z2 = eyeY*sin(angle) + eyeZ*cos(angle); 
		eyeY=y2;
		eyeZ=z2;
		break;
	case GLUT_KEY_DOWN :

		y2 = eyeY*cos(-angle) - eyeZ*sin(-angle);
		z2 = eyeY*sin(-angle) + eyeZ*cos(-angle); 
		eyeY=y2;
		eyeZ=z2;
		break;
	}
}

int main(int argc, char **argv) {

	// init GLUT and create window
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(0,0);
	glutInitWindowSize(800,640);
	glutCreateWindow("comp371 project");
	glEnable(GL_DEPTH_TEST);
	//glEnable(GL_LIGHTING);
	//glEnable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);
	glShadeModel(GL_SMOOTH);
	glEnable (GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

	for(int i =0;i<100;i++){
		buildingHeights[i] = rand()%10+10;
		xBallOffSets[i] = rand()%9-5.5;
	}
	// register callbacks
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutIdleFunc(renderScene);

	// here are the new entries
	glutKeyboardFunc(processNormalKeys);
	glutSpecialFunc(processSpecialKeys);

	// enter GLUT event processing cycle
	glutMainLoop();

	return 1;
}